﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Text;

namespace RAP.Framework.Libary.Utils.Extension
{
	/// <summary>
	/// 将键值对对象的值复制到指定对象的属性
	/// </summary>
	/// <typeparam name="TTarget"></typeparam>
	public static class IDictionaryClone<TDictionary, TTarget> where TDictionary : IDictionary
	{
		private static readonly Func<TDictionary, TTarget, TTarget> funcClone = CreateCloneFunc();

		/// <summary>
		/// 
		/// </summary>
		/// <returns></returns>
		private static Func<TDictionary, TTarget, TTarget> CreateCloneFunc( )
		{
			var typeSrc = typeof( TDictionary );
			var typeTarget = typeof( TTarget );
			var parameterSrcExp = Expression.Parameter( typeSrc, "p" );
			var parameterTargetExp = Expression.Parameter( typeTarget, "t" );
			var propertyExps = new List<Expression>();
			var indexExps = new List<Expression>();

			var returnTarget = Expression.Label( typeTarget );
			foreach ( var p in typeTarget.GetProperties() )
			{
				if ( !p.CanWrite )
				{
					continue;
				}
				object key = p.Name;
				var propertySrcExp = Expression.MakeIndex( parameterSrcExp, typeSrc.GetProperty( "Item" ), new[] { Expression.Constant( key, typeof( object ) ) } );
				var propertyTargetExp = Expression.Property( parameterTargetExp, p );
				var testCondition = Expression.And( Expression.NotEqual( propertySrcExp, Expression.Constant( null ) ), Expression.NotEqual( propertySrcExp, Expression.Constant( string.Empty ) ) );
				var assignValueWithCondition = Expression.IfThen( testCondition, Expression.Assign( propertyTargetExp, Expression.Convert( propertySrcExp, p.PropertyType ) ) );
				propertyExps.Add( assignValueWithCondition );
			}

			propertyExps.Add( Expression.Return( returnTarget, parameterTargetExp, typeTarget ) );
			propertyExps.Add( Expression.Label( returnTarget, Expression.Constant( null, typeTarget ) ) );
			var exp = Expression.Block( propertyExps.ToArray() );
			var lambda = Expression.Lambda<Func<TDictionary, TTarget, TTarget>>( exp, new ParameterExpression[]
			{
				parameterSrcExp,
				parameterTargetExp
			} );
			return lambda.Compile();
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="srcDict"></param>
		/// <param name="targetObj"></param>
		/// <returns></returns>
		public static TTarget Clone( TDictionary srcDict, TTarget targetObj )
		{
			return funcClone( srcDict, targetObj );
		}
	}
}
